package org.gazzax.labs.solrwithmarc.index.processors;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.SolrInputField;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.update.AddUpdateCommand;
import org.apache.solr.update.processor.UpdateRequestProcessor;
import org.apache.solr.update.processor.UpdateRequestProcessorFactory;

/**
 * Factory for {@link FilterImpl}s.
 * 
 * N.B.: At the moment rules (i.e. tags and corresponding indicators) are hard-coded.
 * 
 * @author agazzarini
 * @since 1.0
 */
public class NonFillingCharactersUpdateRequestProcessorFactory extends UpdateRequestProcessorFactory 
{
	private final static String DEFAULT_FIELD_SUFFIX = "-without-nonfilling-chars";
	private final static Map<String, Integer> tagsAndIndicators = new HashMap<String, Integer>();
	static 
	{
		tagsAndIndicators.put("130a", 1);
		tagsAndIndicators.put("630a", 1);
		tagsAndIndicators.put("730a", 1);
		tagsAndIndicators.put("740a", 1);
		tagsAndIndicators.put("210a", 2);
		tagsAndIndicators.put("222a", 2);
		tagsAndIndicators.put("240a", 2);
		tagsAndIndicators.put("242a", 2);
		tagsAndIndicators.put("243a", 2);
		tagsAndIndicators.put("245a", 2);
		tagsAndIndicators.put("247a", 2);
		tagsAndIndicators.put("830a", 2);
		tagsAndIndicators.put("440a", 2);
	}

	/**
	 * An {@link UpdateRequestProcessor} that deletes non filling characters from text (i.e. titles).
	 * 
	 * @author agazzarini
	 * @since 1.0
	 */
	class NonFillingCharactersFilter extends UpdateRequestProcessor 
	{
		/**
		 * Builds a new filter with the given next processor.
		 * 
		 * @param next the next processor in the chain.
		 */
		NonFillingCharactersFilter(final UpdateRequestProcessor next) 
		{
			super(next);
		}

		@Override
		public void processAdd(final AddUpdateCommand cmd) throws IOException 
		{
			final SolrInputDocument document = cmd.getSolrInputDocument();
			
			for (Entry<String, Integer> entry : tagsAndIndicators.entrySet()) 
			{
				processField(getField(entry.getKey(), document), document);
			}
			
			super.processAdd(cmd);
		}

		/**
		 * Once detected, the {@link SolrInputField} is processed in order to see if skipping needs to be applied. 
		 * 
		 * @param field the {@link SolrInputField} that needs to ne processed by this filter.
		 */
		@SuppressWarnings("unchecked")
		private void processField(final SolrInputField field, final SolrInputDocument document) 
		{
			if (field == null)
			{
				return;
			}
			
			final String fieldName = field.getName();
 
			final Integer indicator = tagsAndIndicators.get(fieldName.substring(0, 4));
			if (indicator != null)
			{
				final int howManyNonFillingChars = Character.digit(fieldName.charAt(fieldName.length() - (3 - indicator)), 10);

				final Object value = field.getValue();
				if (value == null) { return; }
	
				if (value instanceof Collection<?> && !((Collection<?>)value).isEmpty()) 
				{
					final Collection<Object> newValueSet = new ArrayList<Object>(((Collection<Object>)value).size());
					for (Iterator<Object> iterator = ((Collection<Object>)value).iterator(); iterator.hasNext();) 
					{
						newValueSet.add(String.valueOf(value).substring(howManyNonFillingChars));
					}
					document.setField(fieldName.substring(0, 4) + DEFAULT_FIELD_SUFFIX, newValueSet, field.getBoost());
				} else 
				{
					document.setField(
							fieldName.substring(0, 4) + DEFAULT_FIELD_SUFFIX, 
							String.valueOf(value).substring(howManyNonFillingChars),
							field.getBoost());
				}
			}
		}
		
		/**
		 * Returns (if exists) a {@link SolrInputField} that has a name starting with the given prefix.
		 * 
		 * @param prefix the tag prefix.
		 * @param document the incoming {@link SolrInputDocument} that needs preprocessing by this filter.
		 * @return the {@link SolrInputField} or null if there's no field with that name (i.e. prefix).
		 */
		private SolrInputField getField(final String prefix, final SolrInputDocument document) 
		{
			for (String fieldName : document.getFieldNames()) 
			{
				if (fieldName.startsWith(prefix)) 
				{
					return document.getField(fieldName);
				}
			}
			return null;
		}
	}
	
	@Override
	public UpdateRequestProcessor getInstance(
			final SolrQueryRequest request,
			final SolrQueryResponse response, 
			final UpdateRequestProcessor next) 
	{
		return new NonFillingCharactersFilter(next);
	}
}